package com.emeraldparser.symbol;

import java.util.Collection;
import java.util.Iterator;

import com.google.common.collect.Lists;

public class SymbolClassScanner<I, O> implements SymbolClassVisitor<I, O> {

	public O scan(SymbolClass symbolClass, I input) {
		return symbolClass.accept(this, input);
	}

	public Collection<O> scan(Iterable<? extends SymbolClass> symbolClasses, I input) {
		Collection<O> outputs = Lists.newArrayList();
		
		for (SymbolClass symbolClass : symbolClasses) {
			outputs.add(scan(symbolClass, input));
		}
		
		return outputs;
	}

	public O reduce(Iterable<? extends O> outputs) {
		Iterator<? extends O> iterator = outputs.iterator();
		
		O output;
		if (iterator.hasNext()) {
			output = iterator.next();
			while (iterator.hasNext()) {
				output = reduce(output, iterator.next());
			}
		} else {
			output = null;
		}
		
		return output;
	}

	public O reduce(O output1, O output2) {
		return output1;
	}

	@Override
	public O visitAlternationSymbolClass(AlternationSymbolClass alternationSymbolClass, I input) {
		return reduce(scan(alternationSymbolClass.getAlternatedSymbolClasses(), input));
	}

	@Override
	public O visitCharacterRangeSymbolClass(CharacterRangeSymbolClass characterRangeSymbolClass, I input) {
		return null;
	}

	@Override
	public O visitCharacterSymbolClass(CharacterSymbolClass characterSymbolClass, I input) {
		return null;
	}

	@Override
	public O visitCharSequenceSymbolClass(CharSequenceSymbolClass charSequenceSymbolClass, I input) {
		return null;
	}

	@Override
	public O visitConcatenationSymbolClass(ConcatenationSymbolClass concatenationSymbolClass, I input) {
		return reduce(scan(concatenationSymbolClass.getConcatenatedSymbolClasses(), input));
	}

	@Override
	public O visitConjunctionSymbolClass(ConjunctionSymbolClass conjunctionSymbolClass, I input) {
		return reduce(scan(conjunctionSymbolClass.getConjunctedSymbolClasses(), input));
	}

	@Override
	public O visitEndOfInputSymbolClass(EndOfInputSymbolClass endOfInputSymbolClass, I input) {
		return null;
	}

	@Override
	public O visitNamedSymbolClass(NamedSymbolClass namedSymbolClass, I input) {
		return null;
	}

	@Override
	public O visitNegationSymbolClass(NegationSymbolClass negationSymbolClass, I input) {
		return scan(negationSymbolClass.getNegatedSymbolClass(), input);
	}

	@Override
	public O visitOptionalSymbolClass(OptionalSymbolClass optionalSymbolClass, I input) {
		return scan(optionalSymbolClass.getOptionalSymbolClass(), input);
	}

	@Override
	public O visitRepetitionSymbolClass(RepetitionSymbolClass repetitionSymbolClass, I input) {
		return scan(repetitionSymbolClass.getRepeatedSymbolClass(), input);
	}

	@Override
	public O visitStartOfInputSymbolClass(StartOfInputSymbolClass startOfInputSymbolClass, I input) {
		return null;
	}

}
